package team3;

import java.util.ArrayList;
import java.util.List;

/**Represents and controls one full game of Reversi.
 * <p>
 * Acts as an ocheastrator for Reversi. Given two AI players and a command to
 * start, the GameManager will (in a seperate thread) construct a starting
 * state, and request the each player make a move, until the game has finished.
 * <p>
 * The GameManager should run in it's own thread, returning from
 * <code>startGameLoop()</code> almost instantly. This allows for the game to run
 * independently of any constraints from the UI.
 * <p>
 * To facilitate interfacing with a UI, the game manager provides a listener
 * interface which it will fire off when appropriate. Any UI that wants to
 * interface with the game will need to implement a GameListener interface.
 * <p>
 * Expected Usage: <br />
 * <code>
 * GameManager manager=new GameManager(blackAI, whiteAI); <br />
 * manager.startGameLoop();
 * </code>
 * <p>
 * Though not enforced, a GameManager should only be used once, meaning that
 * <code>startGameLoop</code> or <code>run</code> should be called at most one
 * time.
 * @see team3.GameListener
 */
public class GameManager implements Runnable
{
	private BoardState currentState;
	private AI.Player currentPlayer;
	private project2.AI playerBlack;
	private project2.AI playerWhite;
	private List<GameListener> listeners;

    /**Constructs a GameManager, and makes it ready for play.
     * @param black The AI that will play for black.
     * @param white The AI that will play for white.
     */
	public GameManager(project2.AI black, project2.AI white)
	{
		currentState = BoardState.getStartingBoard();
		currentPlayer = AI.Player.BLACK;
		playerBlack = black;
		playerWhite = white;
		listeners=new ArrayList<GameListener>();
	}

    /**Starts a thread with which to run the game loop, then returns.
     */
	public void startGameLoop()
	{
		Thread t=new Thread(this);
		t.start();
	}

    /**The game loop. Runs through one game to completion.
     * <p>
     * Starting with the starting player, requests that each player make a move
     * until the game has been finished, firing a <code>nextPlayersTurn</code>
     * event just before a player is asked to make a move. At the end, fires
     * a <code>gameOver</code> event, with the winning player.
     */
	public void run()
	{
		int[] lastMove = BoardState.INVALID_MOVE;
		//keep running until someone wins;
		while(!currentState.isGameFinished())
		{
			fireNextPlayersTurn(currentPlayer, lastMove);

            project2.AI player = getCurrentAI();
			int[] move = player.makeMove(currentState.cloneState(), lastMove);

            //update state of the manager for the given move, select next player
			synchronized(this)
			{
				currentState = currentState.makeMove(currentPlayer, move);
				lastMove = move;
				currentPlayer = (currentPlayer==AI.Player.BLACK) ? AI.Player.WHITE : AI.Player.BLACK;
			}
		}
		fireGameOver(currentState.getWinner());
	}

    /** Fires the game over event for all listeners.
     * @param winner The player that won the game.
     */
	private void fireGameOver(AI.Player winner)
	{
        synchronized(this)
        {
            for(GameListener listener : listeners)
                listener.gameOver(this, winner);
        }
	}

    /** Fires the next players turn event for all listeners.
     * @param player The player whose turn it is now.
     * @param lastMove The move made by the previous player.
     */
	private void fireNextPlayersTurn(AI.Player player, int[] lastMove)
	{
        synchronized(this)
        {
            for(GameListener listener : listeners)
                listener.nextPlayersTurn(this, player, lastMove);
        }
	}

    /** Registers a game listener for event firing.
     * @param listener The listener to register.
     */
	public void registerGameListener(GameListener listener)
	{
        synchronized(this)
        {
    		listeners.add(listener);
        }
	}

    /** @return The AI whose turn it is.
     */
	public project2.AI getCurrentAI()
	{
		synchronized(this)
		{
			if(currentPlayer==AI.Player.BLACK)
				return playerBlack;
			return playerWhite;
		}
	}

    /** @return The BoardState of the game.
     */
	public BoardState getBoard()
	{
		synchronized(this)
		{
			return new BoardState(currentState.cloneState());
		}
	}
}
